﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using System.Web.Routing;
using jp110311.StorageModels;
using MvcWebRole1.Helpers;
using MvcWebRole1.Models;

namespace MvcWebRole1.Controllers
{
    public class BbsController : BaseController
    {
        private static readonly int PageSize = 10;


        private IBbsEntityServiceInvoker invoker;


        public BbsController()
        {
        }

        public BbsController(IBbsEntityServiceInvoker invoker)
        {
            this.invoker = invoker;
        }


        protected override void Initialize(RequestContext requestContext)
        {
            if (invoker == null)
            {
                invoker = new BbsEntityServiceInvoker();
            }

            base.Initialize(requestContext);
        }

        // 指定郵便番号での列挙
        // GET: /Bbs/{zipCode}/{page}
        [Authorize]
        public ActionResult Index(string zipCode, int? page)
        {
            var model = new BbsAreaIndexModel();
            model.ZipCode = zipCode ?? BbsHelper.GetZipCodeFromUser(User);
            model.PageSize = PageSize;
            model.CurrentPage = (page.HasValue && page.Value > 0) ? page.Value : 1;
            model.Total = invoker.GetBbsEntitiesCount(model.ZipCode);
            var skipCount = (model.CurrentPage - 1) * model.PageSize;
            foreach (var entity in invoker.GetBbsEntities(model.ZipCode, skipCount, model.PageSize))
            {
                model.Messages.Add(entity);
            }
            return View(model);
        }

        // 指定郵便番号+タグでの列挙
        // GET: /Bbs/{zipCode}/Search/{tag}/{page}
        [Authorize]
        public ActionResult Search(string zipCode, string tag, int? page)
        {
            var model = new BbsAreaIndexModel();
            model.ZipCode = zipCode ?? BbsHelper.GetZipCodeFromUser(User);
            model.Tag = tag;
            model.PageSize = PageSize;
            model.CurrentPage = (page.HasValue && page.Value > 0) ? page.Value : 1;
            model.Total = invoker.GetBbsTagedEntitiesCount(model.ZipCode, tag);
            var skipCount = (model.CurrentPage - 1) * model.PageSize;
            foreach (var entity in invoker.GetBbsEntityByTag(model.ZipCode, tag, skipCount, model.PageSize))
            {
                model.Messages.Add(entity);
            }
            return View(model);
        }

        // タグを列挙
        // GET /Bbs/{zipCode}/Tag
        [Authorize]
        public ActionResult Tags(string zipCode)
        {
            var model = new BbsTagIndexModel();
            model.ZipCode = zipCode ?? BbsHelper.GetZipCodeFromUser(User);
            foreach (var tag in invoker.GetBbsTags(zipCode, 0, Int32.MaxValue).OrderBy(o => o.RowKey))
            {
                // 一度使われたタグは、削除されてどのメッセージにも付いていない状態になってもデータ上は残っている。
                // 使われているタグだけを表示するために、メッセージ数が0のものは除外している。　
                if (tag.MessageCount != 0)
                {
                    model.Tags.Add(tag);
                }
            }
            return View(model);
        }

        // GET /Bbs/{zipCode}/Post
        [Authorize]
        public ActionResult Post(string zipCode)
        {
            var model = new BbsPostModel();
            model.ZipCode = zipCode ?? BbsHelper.GetZipCodeFromUser(User);
            return View(model);
        }

        // POST /Bbs/{zipCode]/Post
        [Authorize]
        [HttpPost]
        public ActionResult Post(BbsPostModel model)
        {
            if (ModelState.IsValid)
            {
                var postAt = DateTime.Now;
                var authorZipCode = BbsHelper.GetZipCodeFromUser(User);
                var authorNickName = BbsHelper.GetNickNameFromUser(User);
                var bbsMsgId = invoker.AddBbsMessage(model.ZipCode, postAt, model.Title, model.Content, authorZipCode, authorNickName);
                AddTagsToBbsEntity(model.ZipCode, bbsMsgId, model.TagList);
                return RedirectToAction("Index", new { zipCode = model.ZipCode });
            }

            return View(model);
        }

        // GET /Bbs/{zipCode}/{bbsMsgId}/AddTag?returnUrl=XXX
        [Authorize]
        public ActionResult AddTag(string zipCode, string bbsMsgId, string returnUrl)
        {
            var model = new BbsTagPostModel();
            model.ZipCode = zipCode ?? BbsHelper.GetZipCodeFromUser(User);
            model.BbsMsgId = bbsMsgId;
            ViewData["ReturnUrl"] = returnUrl;
            return View(model);
        }

        // POST /Bbs/{zipCode}/{bbsMsgId}/AddTag?returnUrl=XXX
        [Authorize]
        [HttpPost]
        public ActionResult AddTag(BbsTagPostModel model, string returnUrl)
        {
            if (ModelState.IsValid)
            {
                AddTagsToBbsEntity(model.ZipCode, model.BbsMsgId, model.TagList);
                return Redirect(returnUrl);
            }

            // ここで再度セットしないと、入力検証エラーになると元の画面に戻れなくなる。
            ViewData["ReturnUrl"] = returnUrl;
            return View(model);
        }

        private void AddTagsToBbsEntity(string zipCode, string bbsMsgId, string tagList)
        {
            foreach (var tag in BbsHelper.SplitTagList(tagList))
            {
                invoker.AddTagToBbsEntity(zipCode, bbsMsgId, tag);
            }
        }

        // POST /Bbs/{zipCode}/{bbsMsgId}/RemoveTag/{tag}?returnUrl=XXX
        [Authorize]
        public ActionResult RemoveTag(string zipCode, string bbsMsgId, string tag, string returnUrl)
        {
            invoker.RemoveTagFromBbsEntity(zipCode, bbsMsgId, tag);
            return Redirect(returnUrl);
        }

        // BbsEntityServiceの各種機能の呼び出しインターフェース。
        public interface IBbsEntityServiceInvoker
        {
            IEnumerable<BbsEntity> GetBbsEntities(string zipCode, int skipCount, int maxCount);
            IEnumerable<BbsEntity> GetBbsEntityByTag(string zipCode, string tag, int skipCount, int maxCount);
            int GetBbsEntitiesCount(string zipCode);
            int GetBbsTagedEntitiesCount(string zipCode, string tag);
            IEnumerable<BbsTagEntity> GetBbsTags(string zipCode, int skipCount, int maxCount);
            string AddBbsMessage(string zipCode, DateTime postAt, string title, string content, string authorZipCode, string authorNick);
            string AddBbsMessage(string zipCode, DateTime postAt, string title, string content, string authorZipCode, string authorNick, string tag);
            void AddTagToBbsEntity(string zipCode, string bbsMsgId, string tag);
            void RemoveTagFromBbsEntity(string zipCode, string bbsMsgId, string tag);
        }


        // 単純にBbsEntityServicesを呼び出して処理をさせるクラス。
        private class BbsEntityServiceInvoker : IBbsEntityServiceInvoker
        {
            public IEnumerable<BbsEntity> GetBbsEntities(string zipCode, int skipCount, int maxCount)
            {
                return BbsEntityServices.GetBbsEntities(zipCode, skipCount, maxCount);
            }

            public IEnumerable<BbsEntity> GetBbsEntityByTag(string zipCode, string tag, int skipCount, int maxCount)
            {
                return BbsEntityServices.GetBbsEntityByTag(zipCode, tag, skipCount, maxCount);
            }

            public int GetBbsEntitiesCount(string zipCode)
            {
                return BbsEntityServices.GetBbsEntitiesCount(zipCode);
            }

            public int GetBbsTagedEntitiesCount(string zipCode, string tag)
            {
                return BbsEntityServices.GetBbsTagedEntitiesCount(zipCode, tag);
            }

            public IEnumerable<BbsTagEntity> GetBbsTags(string zipCode, int skipCount, int maxCount)
            {
                return BbsEntityServices.GetBbsTags(zipCode, skipCount, maxCount);
            }

            public string AddBbsMessage(string zipCode, DateTime postAt, string title, string content, string authorZipCode, string authorNick)
            {
                return BbsEntityServices.AddBbsMessage(zipCode, postAt, title, content, authorZipCode, authorNick);
            }

            public string AddBbsMessage(string zipCode, DateTime postAt, string title, string content, string authorZipCode, string authorNick, string tag)
            {
                return BbsEntityServices.AddBbsMessage(zipCode, postAt, title, content, authorZipCode, authorNick, tag);
            }

            public void AddTagToBbsEntity(string zipCode, string bbsMsgId, string tag)
            {
                BbsEntityServices.AddTagToBbsEntity(zipCode, bbsMsgId, tag);
            }

            public void RemoveTagFromBbsEntity(string zipCode, string bbsMsgId, string tag)
            {
                BbsEntityServices.RemoveTagFromBbsEntity(zipCode, bbsMsgId, tag);
            }
        }
    }
}
